﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace OpenWaves.ImageTransformations
{
    public class GdiImageTransformationService : IImageTransformationService
    {
        public void TransformImage(Stream input, Stream output, ImageFormat outputFormat, IImageTransformation transformation)
        {
            using (var image = Image.FromStream(input))
            {
                var parameters = new GdiTransformationParameters(image.Width, image.Height);

                transformation.ApplyToImage(parameters);

                TransformImage(image, output, outputFormat, parameters);
            }
        }

        private static void TransformImage(Image input, Stream output, ImageFormat outputFormat, GdiTransformationParameters parameters)
        {
            using (var outputImage = new Bitmap(parameters.Width, parameters.Height, GetValidPixelFormat(input)))
            {
                outputImage.SetResolution(input.HorizontalResolution, input.VerticalResolution);

                using (Graphics graphics = Graphics.FromImage(outputImage))
                {
                    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    graphics.CompositingMode = CompositingMode.SourceCopy;
                    
                    using (var imageAttributes = new ImageAttributes())
                    {
                        imageAttributes.SetWrapMode(WrapMode.TileFlipXY);

                        graphics.DrawImage(
                            input,
                            new Rectangle(0, 0, parameters.Width, parameters.Height),
                            parameters.SourceRectangle.X,
                            parameters.SourceRectangle.Y,
                            parameters.SourceRectangle.Width,
                            parameters.SourceRectangle.Height,
                            GraphicsUnit.Pixel,
                            imageAttributes);
                    }

                    outputImage.Save(output, GetImageFormat(outputFormat));
                }
            }
        }

        private static System.Drawing.Imaging.ImageFormat GetImageFormat(ImageFormat outputFormat)
        {
            switch (outputFormat)
            {
                case ImageFormat.Jpeg:
                    return System.Drawing.Imaging.ImageFormat.Jpeg;
                case ImageFormat.Png:
                    return System.Drawing.Imaging.ImageFormat.Png;
                default:
                    throw new ArgumentException(ExceptionMessage.Format("Invalid ImageFormat: {0}", outputFormat), "outputFormat");
            }
        }

        private static PixelFormat GetValidPixelFormat(Image sourceImage)
        {
            if (IsValidPixelFormat(sourceImage.PixelFormat) == false)
            {
                return PixelFormat.Format32bppRgb;
            }

            //like gif
            bool isIndexedPixelFormat = (sourceImage.PixelFormat & PixelFormat.Indexed) != PixelFormat.Undefined;

            if (isIndexedPixelFormat)
            {
                return PixelFormat.Format32bppRgb;
            }

            //fix for 48 bit with transparency which is differently treated on win2003 (schibsted?)
            if (sourceImage.PixelFormat == PixelFormat.Format48bppRgb)
            {
                return PixelFormat.Format32bppArgb;
            }


            return sourceImage.PixelFormat;
        }

        internal static bool IsValidPixelFormat(PixelFormat pixelFormat)
        {
            var isDefined = Enum.GetValues(typeof(PixelFormat))
                .Cast<PixelFormat>()
                .Contains(pixelFormat);

            if (isDefined == false)
            {
                return false;
            }

            //http://msdn.microsoft.com/en-us/library/9w3wxk5x(vs.71).aspx
            var notSupportedFormats = new[]
                                          {
                                              PixelFormat.Undefined,
                                              PixelFormat.DontCare,
                                              PixelFormat.Format16bppArgb1555,
                                              PixelFormat.Format16bppGrayScale
                                          };


            return notSupportedFormats.Contains(pixelFormat) == false;
        }
    }
}